Logo de islavisual
Isotipo de islavisual IslaVisual
imagen de sección

Ultima revisión 23/12/2020

Cómo crear imágenes SVG accesibles y personalizables

Aunque muchos saben crear y manipular documentos e imágenes en SVG, no todos conocen la potencia que tiene. Hoy os voy a mostrar cómo es posible crear una imagen SVG accesible y que, además, sea personalizable a partir de parámetros proporcionados por URL.

Imaginemos, por ejemplo, que tenemos que crear el típico icono de prohibición rojo con un texto que pone "No acceder". El código podría ser algo como:

<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100%" height="100%" viewBox="0 0 256 256" preserveAspectRatio="xMidYMid meet">
    <defs xmlns="http://www.w3.org/2000/svg">
        <style type="text/css">
            @import url('https://fonts.googleapis.com/css2?family=Oswald:wght@500');
            svg  { background-color: #ffffff; }
            text { letter-spacing: 10px; }
        </style>
    </defs>

    <g id="layer101" fill="#e00202" stroke="none">
        <path d="m210 128c0 57.99-47.01 105-105 105s-105-47.01-105-105 47.01-105 105-105 105 47.01 105 105z" />
        <path xmlns="http://www.w3.org/2000/svg" d="m10 112.36h190v35h-190z" fill="#fff"/>
    </g>

    <g id="layer102" fill="#fff" font-family="Oswald" font-weight="500" stroke="none">
            <text xmlns="http://www.w3.org/2000/svg" x="70" y="96" font-size="50">NO</text>
            <text xmlns="http://www.w3.org/2000/svg" x="32" y="182" font-size="25">ACCEDER</text>
    </g>
</svg>

Hasta aquí todo genial. Ya tenemos el icono generado en SVG y se ve estupendamente, eso sí, siempre que lo mostremos con un tamaño suficiente de 256x256 píxeles. El problema viene cuando tenemos que mostrarlo a un tamaño típico de icono (por ejemplo, en 32x32 píxeles) porque el texto incrustado se vuelve ilegible y molesta más que ninguna otra cosa. ¿Qué hacer? Lo que primero se le ocurrirá a muchos es crear una nueva imagen sin el texto, aunque eso suponga tener dos imágenes que cargar y dos imágenes que mantener.

No obstante, existe otra opción más eficiente que es la que vamos a utilizar. Consiste en añadir un JavaScript al final del propio documento SVG que pregunte por uno u otro parámetro y ejecute la funcionalidad solicitada según lo que venga establecido por la URL. Por ejemplo, como el texto no se lee cuando mostramos la imagen miniaturizada, vamos a eliminarlo. Para ello, podríamos hacer que si el parámetro "thumb" está establecido, ejecute un código como el siguiente:

if(window.location.search.indexOf("thumb") != -1){
    var aux = document.querySelector("#layer102 text:nth-child(1)");
    while(aux != null){
        aux.parentElement.removeChild(aux);
        aux = document.querySelector("#layer102 text:nth-child(1)");
    }
}

Cierto es que esta opción puede suponer que el archivo tenga un tamaño mayor, pero nos beneficiamos del posible cacheo y, si sumamos el tamaño de ambos archivos, el que tiene texto, y el que no, seguramente ocupen bastante más que la opción de incluir un pequeño script de JavaScript.

Ahora, imaginemos que nos surge la necesidad de mostrar la imagen con los colores invertidos. Para esta casuística lo que haremos es preguntar por el parámetro "invert" de modo que, si está establecido, cambiaremos el color del texto por el del fondo, y el color de fondo por el del texto.

Al igual que antes, podríamos crear otro archivo SVG (con lo que ya tendríamos tres archivos para cargar y mantener), por lo que lo mejor es añadir a nuestro código JavaScript otra condición.

if(window.location.search.indexOf("invert") != -1){
    var l1 = document.querySelector("#layer101");
    var l2 = document.querySelector("#layer102");
    var aux = window.getComputedStyle(l1).fill;

    l1.setAttribute("fill", window.getComputedStyle(l2).fill);
    l2.setAttribute("fill", aux);
    document.querySelector("svg").style.backgroundColor = aux;

    l1.querySelector("path:nth-child(2)").setAttribute("fill", aux);
}

Ya tenemos una imagen que puede ser mostrada de tres maneras diferentes. Por tanto, sólo nos resta hacer que este documento sea accesible. Una de las formas de hacerlo es:

El resultado final debería ser algo como:

<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg role="img" aria-labelledby="title-desc" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100%" height="100%" viewBox="0 0 256 256" preserveAspectRatio="xMidYMid meet">
    <title id="title-desc">
        Icono de acceso prohibido con el texto "No acceder" incrustado
    </title>

    <defs xmlns="http://www.w3.org/2000/svg">
        <style type="text/css">
            @import url('https://fonts.googleapis.com/css2?family=Oswald:wght@500');
            svg  { background-color: #ffffff; }
            text { letter-spacing: 10px; }
        </style>
    </defs>

    <g id="layer101" fill="#e00202" stroke="none">
        <path d="m210 128c0 57.99-47.01 105-105 105s-105-47.01-105-105 47.01-105 105-105 105 47.01 105 105z" />
        <path xmlns="http://www.w3.org/2000/svg" d="m10 112.36h190v35h-190z" fill="#fff"/>
    </g>

    <g id="layer102" fill="#fff" 
    font-family="Oswald" font-weight="500"
    stroke="none">
            <text xmlns="http://www.w3.org/2000/svg" x="70" y="96" font-size="50">NO</text>
            <text xmlns="http://www.w3.org/2000/svg" x="32" y="182" font-size="25">ACCEDER</text>
    </g>

    <script>
        //<![CDATA[
            if(window.location.search.indexOf("thumb") != -1){
                var aux = document.querySelector("#layer102 text:nth-child(1)");
                while(aux != null){
                    aux.parentElement.removeChild(aux);
                    aux = document.querySelector("#layer102 text:nth-child(1)");
                }
            }
            
            if(window.location.search.indexOf("invert") != -1){
                var l1 = document.querySelector("#layer101");
                var l2 = document.querySelector("#layer102");
                var aux = window.getComputedStyle(l1).fill;

                l1.setAttribute("fill", window.getComputedStyle(l2).fill);
                l2.setAttribute("fill", aux);
                document.querySelector("svg").style.backgroundColor = aux;

                l1.querySelector("path:nth-child(2)").setAttribute("fill", aux);
            }
        //]]>
    </script>
</svg>

Nota: Este tipo de documentos SVG sólo pueden cargarse a través del elemento OBJECT de HTML5. Aunque sea una imagen, lleva código ejecutable, por lo que si utilizamos el elemento IMG no funcionarán los posibles parámetros. Un ejemplo de uso podría ser:

<object data="http://localhost/2020/prohibited.svg" style="width: 32px;">

Más información sobre SVG y maquetación en el libro Diseño y construcción de páginas Web, de la editorial RA-MA y disponible también en Amazon.

Sobre el autor

Imagen de Pablo Enrique Fernández Casado
Pablo Enrique Fernández Casado

CEO de IslaVisual, Manager, Full Stack Analyst Developer y formador por cuenta ajena con más de 25 años de experiencia en el campo de la programación y más de 10 en el campo del diseño, UX, usabilidad web y accesibilidad web. También es escritor y compositor de música, además de presentar múltiples soft kills como la escucha activa, el trabajo en equipo, la creatividad, la resiliencia o la capacidad de aprendizaje, entre otras.

Especializado en proveer soluciones integrales de bajo coste y actividades de consultoría de Usabilidad, Accesibilidad y Experiencia de Usuario (UX), además de ofrecer asesoramiento en SEO, optimización de sistemas y páginas web, entre otras habilidades.